package middleware

import (
	"errors"
	"github.com/dgrijalva/jwt-go"
	"github.com/gin-gonic/gin"
	"go-malphite/app/common/constant"
	"go-malphite/app/utils"
	"net/http"
	"strconv"
	"strings"
	"time"
)

// AuthJWT 中间件jwt授权
func AuthJWT() gin.HandlerFunc {
	// 获取jwt，Authorization: "Bearer " + token
	return func(c *gin.Context) {
		authHeader := c.Request.Header.Get("Authorization")
		if authHeader == "" {
			c.JSON(http.StatusOK, gin.H{
				"code": 0,
				"msg":  "请求头缺少Authorization参数",
			})
			c.Abort()
			return
		}
		// 解析jwt
		parts := strings.SplitN(authHeader, " ", 2)
		if !(len(parts) == 2 && parts[0] == "Bearer") {
			c.JSON(http.StatusOK, gin.H{
				"code": 0,
				"msg":  "请求头Authorization参数格式错误",
			})
			c.Abort()
			return
		}
		token := parts[1]
		claims, err := ParseToken(token)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code": 0,
				"msg":  err.Error(),
			})
			c.Abort()
			return
		}
		// 判断redis中token是否已手动退出登录
		_, err = utils.Redis.Get(token).Result()
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code": 0,
				"msg":  "请重新登录！",
			})
			c.Abort()
			return
		}
		currentUserId, err := strconv.ParseInt(claims.UserId, 10, 64)
		if err != nil {
			c.JSON(http.StatusOK, gin.H{
				"code": 0,
				"msg":  "用户id格式错误！",
			})
			c.Abort()
			return
		}
		c.Set(constant.CurrentUserId, token)
		c.Set(constant.CurrentUserId, currentUserId)
		c.Next()
	}
}

type MyClaims struct {
	UserId string `json:"userId"`
	jwt.StandardClaims
}

const TokenExpiredDuration = 60 * 60 * 24 * 30 * time.Second

var MySecretKey = []byte("gin-jwt-key-123456")

// GenerateToken 生成token
func GenerateToken(userId string) (string, error) {
	claims := MyClaims{
		userId,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(TokenExpiredDuration).Unix(),
		},
	}
	t := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
	return t.SignedString(MySecretKey)
}

// ParseToken 解析token
func ParseToken(tokenString string) (*MyClaims, error) {
	token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
		return MySecretKey, nil
	})
	if err != nil {
		return nil, errors.New("token解析失败")
	}
	if claims, ok := token.Claims.(*MyClaims); ok && token.Valid {
		expireTime := time.Now().Unix() - claims.ExpiresAt
		if int(expireTime) >= 0 {
			return nil, errors.New("token已过期")
		}
		return claims, nil
	}
	return nil, errors.New("无效token")
}
